home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 August: Tool Chest / Dev.CD Aug 98 TC.toast / Sample Code / Networking / OTStreamLogViewer1.0b1 / IC Libraries / ICDialogs.p < prev    next >
Encoding:
Text File  |  1998-03-15  |  24.8 KB  |  765 lines  |  [TEXT/CWIE]

  1. unit ICDialogs;
  2.  
  3. (*    This file is part of the Internet Configuration system and is placed in the public domain for the benefit of all.
  4.  
  5.     This module implements many many tiny little routines that are of great assistance
  6.     to those who use the Dialog Manager as a user interface.  Most of these routines
  7.     are pretty obvious, so there's not a lot of comments in the interface section.
  8.     However there may be comments in the implementation section that you might
  9.     find interesting.
  10. *)
  11.  
  12. interface
  13.  
  14.     uses
  15.         Quickdraw,
  16.         Dialogs;
  17.     
  18.     const
  19.         ditOK = 1;
  20.         ditCancel =2;
  21.         ditDontSave = 3;
  22.         
  23.     (* ***** Better Getters and Setters ***** *)
  24.         
  25.     procedure GetDItemText (dlg: DialogPtr; item: integer; var text: Str255);
  26.     procedure SetDItemText (dlg: DialogPtr; item: integer; text: Str255);
  27.  
  28.     procedure GetDItemKind (dlg: DialogPtr; item: integer; var kind: integer);
  29.     procedure SetDItemKind (dlg: DialogPtr; item: integer; kind: integer);
  30.     function GetDItemHandle (dlg: DialogPtr; item: integer): Handle;
  31.     procedure SetDItemHandle (dlg: DialogPtr; item: integer; itemH: univ Handle);
  32.     procedure GetDItemRect (dlg: DialogPtr; item: integer; var itemRect: Rect);
  33.     procedure SetDItemRect (dlg: DialogPtr; item: integer; const itemRect: Rect);
  34.  
  35.     (* ***** Dialog Control Getters and Setters ***** *)
  36.  
  37.     function GetDControlHandle (dlg: DialogPtr; item: integer): ControlHandle;
  38.     
  39.     function GetDControlEnable (dlg: DialogPtr; item: integer): Boolean;
  40.     procedure SetDControlEnable (dlg: DialogPtr; item: integer; enable: Boolean);
  41.     function GetDControlTitle (dlg: DialogPtr; item: integer): Str255;
  42.     procedure SetDControlTitle (dlg: DialogPtr; item: integer; title: Str255);
  43.     function GetDControlValue (dlg: DialogPtr; item: integer): integer;
  44.     procedure SetDControlValue (dlg: DialogPtr; item: integer; value: integer);
  45.     function GetDControlMaximum (dlg: DialogPtr; item: integer): integer;
  46.     procedure SetDControlMaximum (dlg: DialogPtr; item: integer; value: integer);
  47.     function GetDControlBoolean (dlg: DialogPtr; item: integer): Boolean;
  48.     procedure SetDControlBoolean (dlg: DialogPtr; item: integer; value: Boolean);
  49.     procedure ToggleDControlBoolean (dlg: DialogPtr; item: integer);
  50.  
  51.     (* ***** Default Button Support ***** *)
  52.  
  53.     var
  54.         gDefaultButtonUPP : UserItemUPP;
  55.         
  56.     procedure DefaultButtonUserItem (dlg: DialogPtr; item: integer);
  57.     procedure SetupDefaultButtonUserItem (dlg: DialogPtr; defaultItem, roundRectUserItem: integer);
  58.     (* See comment in implementation part. *)
  59.     
  60.     (* ***** Cool Filter Functions ***** *)
  61.     
  62.     (* Some of this functionality is subsumed by System 7's standard
  63.         filter functions, but IC can't use them because it has to run
  64.         under System 6.
  65.         
  66.         These routines are exported as both UPP and routines because
  67.         some places in IC (such as filter functions that are layered
  68.         on top of these default filter functions) need to call them directly.
  69.     *)
  70.     
  71.     var
  72.         gOKModalFilterUPP : ModalFilterUPP;
  73.         gOKCancelModalFilter : ModalFilterUPP;
  74.         gOKCancelDiscardModalFilter : ModalFilterUPP;
  75.  
  76.     function OKModalFilter (dlg: DialogPtr; var event: EventRecord; var item: integer): Boolean;
  77.     function OKCancelModalFilter (dlg: DialogPtr; var event: EventRecord; var item: integer): Boolean;
  78.     function OKCancelDiscardModalFilter (dlg: DialogPtr; var event: EventRecord; var item: integer): Boolean;
  79.  
  80.     (* ***** Stuff That Should Be In the Dialog Manager ***** *)
  81.  
  82.     function CountDItems (dlg: DialogPtr): integer;
  83.     function GetSelectedDialogTextItem (dlg: DialogPtr): integer;
  84.     function DialogItemHidden(dlg : DialogPtr; item : integer) : Boolean;
  85.     function GetDPopupMenuHandle (dlg: DialogPtr; item: integer): MenuHandle;
  86.     procedure InvalDItem(dlg : DialogPtr; item : integer);
  87.  
  88.     (* ***** Miscellaneous Stuff ***** *)
  89.     
  90.     procedure DrawStyledString (dlg: DialogPtr; item: integer; styledString: Str255);
  91.     procedure ShiftTab (dlg: DialogPtr);
  92.     procedure FlashDItem (dlg: DialogPtr; item: integer);
  93.     function TrackDItem(window : WindowPtr; item : integer) : Boolean;
  94.  
  95.     (* ***** Initialisation ***** *)
  96.     
  97.     procedure InitICDialogs;
  98.  
  99. implementation
  100.  
  101.     uses
  102.         TextUtils,
  103.         Fonts,
  104.         
  105.         ICDebug,
  106.         ICCommonSubs;
  107.         
  108.     (* ***** Better Getters and Setters ***** *)
  109.  
  110.     procedure GetDItemText (dlg: DialogPtr; item: integer; var text: Str255);
  111.         var
  112.             itemH: Handle;
  113.     begin
  114.         itemH := GetDItemHandle(dlg, item);
  115.         GetDialogItemText(itemH, text);
  116.     end; (* GetDItemText *)
  117.  
  118.     procedure SetDItemText (dlg: DialogPtr; item: integer; text: Str255);
  119.         var
  120.             itemH: Handle;
  121.     begin
  122.         itemH := GetDItemHandle(dlg, item);
  123.         SetDialogItemText(itemH, text);
  124.     end; (* SetDItemText *)
  125.  
  126.     procedure GetDItemKind (dlg: DialogPtr; item: integer; var kind: integer);
  127.         var
  128.             junkRect : Rect;
  129.             junkItemH: Handle;
  130.     begin
  131.         GetDialogItem(dlg, item, kind, junkItemH, junkRect);
  132.     end; (* GetDItemKind *)
  133.  
  134.     procedure SetDItemKind (dlg: DialogPtr; item: integer; kind: integer);
  135.         var
  136.             junkKind: integer;
  137.             itemH: Handle;
  138.             itemRect: Rect;
  139.     begin
  140.         GetDialogItem(dlg, item, junkKind, itemH, itemRect);
  141.         SetDialogItem(dlg, item, kind, itemH, itemRect);
  142.     end; (* SetDItemKind *)
  143.  
  144.     function GetDItemHandle (dlg: DialogPtr; item: integer): Handle;
  145.         var
  146.             itemKind: integer;
  147.             itemH: Handle;
  148.             itemRect: Rect;
  149.     begin
  150.         GetDialogItem(dlg, item, itemKind, itemH, itemRect);
  151.         GetDItemHandle := itemH;
  152.     end; (* GetDItemHandle *)
  153.  
  154.     procedure SetDItemHandle (dlg: DialogPtr; item: integer; itemH: univ Handle);
  155.         var
  156.             itemKind: integer;
  157.             junkItemH: Handle;
  158.             itemRect: Rect;
  159.     begin
  160.         GetDialogItem(dlg, item, itemKind, junkItemH, itemRect);
  161.         SetDialogItem(dlg, item, itemKind, itemH, itemRect);
  162.     end; (* SetDItemHandle *)
  163.  
  164.     procedure GetDItemRect (dlg: DialogPtr; item: integer; var itemRect: Rect);
  165.         var
  166.             itemKind: integer;
  167.             itemH: Handle;
  168.     begin
  169.         GetDialogItem(dlg, item, itemKind, itemH, itemRect);
  170.     end; (* GetDItemRect *)
  171.  
  172.     procedure SetDItemRect (dlg: DialogPtr; item: integer; const itemRect: Rect);
  173.         var
  174.             itemKind: integer;
  175.             itemH: Handle;
  176.             tmpItemRect: Rect;
  177.     begin
  178.         GetDialogItem(dlg, item, itemKind, itemH, tmpItemRect);
  179.         tmpItemRect := itemRect;
  180.         SetDialogItem(dlg, item, itemKind, itemH, tmpItemRect);
  181.     end; (* SetDItemRect *)
  182.  
  183.     (* ***** Dialog Control Getters and Setters ***** *)
  184.  
  185.     function GetDControlHandle (dlg: DialogPtr; item: integer): ControlHandle;
  186.     begin
  187.         GetDControlHandle := ControlHandle(GetDItemHandle(dlg, item));
  188.     end; (* GetDControlHandle *)
  189.  
  190.     function GetDControlEnable (dlg: DialogPtr; item: integer): Boolean;
  191.     begin
  192.         GetDControlEnable := (GetDControlHandle(dlg, item)^^.contrlHilite <> 255);
  193.     end; (* GetDControlEnable *)
  194.  
  195.     procedure SetDControlEnable (dlg: DialogPtr; item: integer; enable: Boolean);
  196.         var
  197.             controlH: ControlHandle;
  198.             newHighlight: integer;
  199.     begin
  200.         controlH := GetDControlHandle(dlg, item);
  201.         newHighlight := (255 * ord(not enable));
  202.         if controlH^^.contrlHilite <> newHighlight then begin
  203.             HiliteControl(controlH, newHighlight);
  204.         end; (* if *)
  205.     end; (* SetDControlEnable *)
  206.  
  207.     function GetDControlTitle (dlg: DialogPtr; item: integer): Str255;
  208.         var
  209.             title: Str255;
  210.     begin
  211.         GetControlTitle(GetDControlHandle(dlg, item), title);
  212.         GetDControlTitle := title;
  213.     end; (* GetDControlTitle *)
  214.  
  215.     procedure SetDControlTitle (dlg: DialogPtr; item: integer; title: Str255);
  216.         var
  217.             controlH: ControlHandle;
  218.             oldTitle: Str255;
  219.     begin
  220.         controlH := GetDControlHandle(dlg, item);
  221.         GetControlTitle(controlH, oldTitle);
  222.         if oldTitle <> title then begin
  223.             SetControlTitle(controlH, title);
  224.         end; (* if *)
  225.     end; (* SetDControlTitle *)
  226.  
  227.     function GetDControlValue (dlg: DialogPtr; item: integer): integer;
  228.     begin
  229.         GetDControlValue := GetControlValue(GetDControlHandle(dlg, item));
  230.     end; (* GetDControlValue *)
  231.  
  232.     procedure SetDControlValue (dlg: DialogPtr; item: integer; value: integer);
  233.     begin
  234.         SetControlValue(GetDControlHandle(dlg, item), value);
  235.     end; (* SetDControlValue *)
  236.  
  237.     function GetDControlMaximum (dlg: DialogPtr; item: integer): integer;
  238.     begin
  239.         GetDControlMaximum := GetControlMaximum(GetDControlHandle(dlg, item));
  240.     end; (* GetDControlMaximum *)
  241.  
  242.     procedure SetDControlMaximum (dlg: DialogPtr; item: integer; value: integer);
  243.     begin
  244.         SetControlMaximum(GetDControlHandle(dlg, item), value);
  245.     end; (* SetDControlMaximum *)
  246.  
  247.     function GetDControlBoolean (dlg: DialogPtr; item: integer): Boolean;
  248.     begin
  249.         GetDControlBoolean := (GetControlValue(GetDControlHandle(dlg, item)) <> 0);
  250.     end; (* GetDControlBoolean*)
  251.  
  252.     procedure SetDControlBoolean (dlg: DialogPtr; item: integer; value: Boolean);
  253.     begin
  254.         SetControlValue(GetDControlHandle(dlg, item), ord(value));
  255.     end; (* SetDControlBoolean *)
  256.  
  257.     procedure ToggleDControlBoolean (dlg: DialogPtr; item: integer);
  258.     begin
  259.         SetDControlBoolean(dlg, item, not GetDControlBoolean(dlg, item));
  260.     end; (* ToggleDControlBoolean *)
  261.  
  262.     (* ***** Default Button Support ***** *)
  263.  
  264.     procedure DefaultButtonUserItem (dlg: DialogPtr; item: integer);
  265.         (* This routine is the user item update proc for the rounded rectangle
  266.             user item that denotes the default button. It tests the enabled
  267.             state of the default button (dialog item number 1) and draws the
  268.             rounded rectangle in either grey or black depending on the state.
  269.  
  270.             Note that the dialog item number of the default button is hard
  271.             coded into this routine.  There will be no exceptions!
  272.             
  273.             This routine is exported because it can be called by the application
  274.             directly when it thinks that the default button highlighting needs to be
  275.             updated.
  276.         *)
  277.         var
  278.             itemRect: Rect;
  279.             greyPat : Pattern;
  280.     begin
  281.         {$unused item}
  282.         SetPort(dlg);
  283.         GetDItemRect(dlg, ditOK, itemRect);
  284.         PenSize(3, 3);
  285.         InsetRect(itemRect, -4, -4);
  286.         if not GetDControlEnable(dlg, ditOK) then begin
  287.  
  288.             // Avoid using "qd.gray" so that we can be compiled
  289.             // into a code resource.  Sometimes programming the Mac
  290.             // just sucks.
  291.             
  292.             StuffHex(@greyPat, 'AA55AA55AA55AA55');
  293.             PenPat(greyPat);
  294.         end; (* if *)
  295.         FrameRoundRect(itemRect, 16, 16);
  296.         PenNormal;
  297.     end; (* DefaultButtonUserItem *)
  298.  
  299.     procedure SetupDefaultButtonUserItem (dlg: DialogPtr; defaultItem, roundRectUserItem: integer);
  300.         (* This routine installs the DefaultButtonUserItem as the user item update
  301.             proc for roundRectUserItem.  It also sets the bounding rect of roundRectUserItem
  302.             to exactly encompass the bounding rect of defaultItem, so that you don't
  303.             have to get it exactly right when editing the dialog.
  304.         *)
  305.         var
  306.             itemRect: Rect;
  307.     begin
  308.         ICAssert(defaultItem = 1);
  309.         GetDItemRect(dlg, defaultItem, itemRect);
  310.         InsetRect(itemRect, -10, -10);
  311.         SetDItemRect(dlg, roundRectUserItem, itemRect);
  312.         SetDItemHandle(dlg, roundRectUserItem, Handle(gDefaultButtonUPP));
  313.     end; (* SetupDefaultButtonUserItem *)
  314.  
  315.     (* ***** Cool Filter Functions ***** *)
  316.  
  317.     function DoButtonKey (dlg: DialogPtr; item: integer;
  318.                                                     var event: EventRecord; var resultItem: integer): Boolean;
  319.         (* This function is used to respond to a keyboard event hitting a dialog
  320.             button.  If the button denoted by dlg and item is enabled, the function
  321.             flashes the button, sets resultItem to item, and returns true.
  322.             Otherwise it beeps and sets event to a null event.
  323.         *)
  324.     begin
  325.         if GetDControlEnable(dlg, item) then begin
  326.             resultItem := item;
  327.             FlashDItem(dlg, item);
  328.             DoButtonKey := true;
  329.         end else begin
  330.             SysBeep(10);
  331.             event.what := nullEvent;
  332.             DoButtonKey := false;
  333.         end;
  334.     end; (* DoButtonKey *)
  335.  
  336.     function OKModalFilter (dlg: DialogPtr; var event: EventRecord; var item: integer): Boolean;
  337.         (* This is pretty much the standard modal dialog filter function.  It handles
  338.             mapping returns and enters to the ditOK button, and also deals with
  339.             shift tab.
  340.         *)
  341.         var
  342.             result : Boolean;
  343.             typedChar: char;
  344.     begin
  345.         result := false;
  346.         if (event.what = keyDown) or (event.what = autoKey) then begin
  347.             typedChar := chr(band(event.message, charCodeMask));
  348.             if (typedChar = kCRChar) or (typedChar = kEnterChar) then begin
  349.                 result := DoButtonKey(dlg, ditOK, event, item);
  350.             end else if (typedChar = kTabChar) and (band(event.modifiers, shiftKey) <> 0) then begin
  351.                 if GetSelectedDialogTextItem(dlg) <> 0 then begin
  352.                     ShiftTab(dlg);
  353.                     result := true;
  354.                 end; (* if *)
  355.             end; (* if *)
  356.         end;
  357.         OKModalFilter := result;
  358.     end; (* OKModalFilter *)
  359.  
  360.     function OKCancelModalFilter (dlg: DialogPtr; var event: EventRecord; var item: integer): Boolean;
  361.         (* This is the standard modal filter for dialogs with an OK and a Cancel button.
  362.             It handles all that OKModalFilter does, and then deals with mapping Escape
  363.             and command-dot to ditCancel.
  364.         *)
  365.         var
  366.             result : Boolean;
  367.             typedChar: char;
  368.     begin
  369.         result := OKModalFilter(dlg, event, item);
  370.         if not result and ((event.what = keyDown) or (event.what = autoKey)) then begin
  371.             typedChar := chr(band(event.message, charCodeMask));
  372.             if ((typedChar = '.') and (band(event.modifiers, cmdKey) <> 0)) 
  373.                     or (typedChar = kEscChar) then begin
  374.                 result := DoButtonKey(dlg, ditCancel, event, item);
  375.             end; (* if *)
  376.         end; (* if *)
  377.         OKCancelModalFilter := result;
  378.     end; (* OKCancelModalFilter *)
  379.  
  380.     function OKCancelDiscardModalFilter (dlg: DialogPtr; var event: EventRecord; var item: integer): Boolean;
  381.         (* This is the standard modal filter for dialogs with an OK, Cancel and Dont Save button.
  382.             It handles all that OKCancelModalFilter does, and then deals with mapping 
  383.             command-D to ditDontSave.
  384.         *)
  385.         var
  386.             result : Boolean;
  387.             typedChar: integer;
  388.     begin
  389.         result := OKCancelModalFilter(dlg, event, item);
  390.         if not result and ((event.what = keyDown) or (event.what = autoKey)) then begin
  391.             typedChar := band(event.message, charCodeMask);
  392.             if (typedChar = ord('d')) and (band(event.modifiers, cmdKey) <> 0) then begin
  393.                 result := DoButtonKey(dlg, ditDontSave, event, item);
  394.             end; (* if *)
  395.         end; (* if *)
  396.         OKCancelDiscardModalFilter := result;
  397.     end; (* OKCancelDiscardModalFilter *)
  398.  
  399.     (* ***** Stuff That Should Be In the Dialog Manager ***** *)
  400.  
  401.     function CountDItems (dlg: DialogPtr): integer;
  402.         (* Identical to CountDITL but works under System 6 without Comms Toolbox installed. *)
  403.     begin
  404.         CountDItems := IntegerPtr(DialogPeek(dlg)^.items^)^ + 1;
  405.     end; (* CountDItems*)
  406.  
  407.     function GetSelectedDialogTextItem (dlg: DialogPtr): integer;
  408.         (* Returns the item number of the edit text that contains the insertion point. *)
  409.     begin
  410.         GetSelectedDialogTextItem := DialogPeek(dlg)^.editField + 1;
  411.     end; (* GetSelectedDialogTextItem *)
  412.  
  413.     function DialogItemHidden(dlg : DialogPtr; item : integer) : Boolean;
  414.         (* Returns true if the item has been hidden with HideDialogItem. *)
  415.         var
  416.             itemRect : Rect;
  417.     begin
  418.         GetDItemRect(dlg, item, itemRect);
  419.         DialogItemHidden := (itemRect.top < 16384) and (itemRect.left < 16384);
  420.     end; (* DialogItemHidden *)
  421.  
  422.     function GetDPopupMenuHandle (dlg: DialogPtr; item: integer): MenuHandle;
  423.         (* Returns the MenuHandle for the popup menu dialog item. This is documented
  424.             as being available from the first 4 bytes of the handle pointed to be
  425.             the item's contrlData field.
  426.         *)
  427.         type
  428.             MenuHandlePtr = ^MenuHandle;
  429.             MenuHandleHandle = ^MenuHandlePtr;
  430.     begin
  431.         GetDPopupMenuHandle := MenuHandleHandle(GetDControlHandle(dlg, item)^^.contrlData)^^;
  432.     end; (* GetDPopupMenuHandle*)
  433.  
  434.     procedure InvalDItem(dlg : DialogPtr; item : integer);
  435.         (* Invalidate a dialog item, ie cause it to be redrawn via an update event. *)
  436.         var
  437.             oldPort : GrafPtr;
  438.             itemRect : Rect;
  439.     begin
  440.         GetPort(oldPort);
  441.         SetPort(dlg);
  442.         GetDItemRect(dlg, item, itemRect);
  443.         InvalRect(itemRect);
  444.         SetPort(oldPort);
  445.     end; (* InvalDItem *)
  446.     
  447.     (* ***** Miscellaneous Stuff ***** *)
  448.  
  449.     function Split (delimiter : Str255; str: Str255; var head, tail: Str255): Boolean;
  450.         (* This routine splits str at the first occurence of delimiter and sets
  451.             head to be the text in front of the delimiter and tail to be the text
  452.             behind it.  It returns true if it could find a delimiter (and hence
  453.             perform the split), otherwise it returns false.
  454.         *)
  455.         var
  456.             delimiterPosition: integer;
  457.     begin
  458.         delimiterPosition := pos(delimiter, str);
  459.         if delimiterPosition > 0 then begin
  460.             head := TPCopy(str, 1, delimiterPosition - 1);
  461.             tail := TPCopy(str, delimiterPosition + length(delimiter), 255);
  462.         end; (* if *)
  463.         Split := delimiterPosition > 0;
  464.     end; (* Split *)
  465.     
  466.     procedure DrawStyledString (dlg: DialogPtr; item: integer; styledString: Str255);
  467.         (* This function draws the dialog item based on the styledString.  styledString
  468.             is a colon delimited set of fields that determines how the string is to be
  469.             drawn.  The general format is:
  470.             
  471.                 font:size:style:justification:text
  472.             
  473.             Any of these fields can be left blank but the colons must remain.
  474.             
  475.             If font is blank, Geneva is used.
  476.             If size is blank, 9 point is used.
  477.             style is a string with 0 or more characters. The characters 0..6
  478.                 turn on the corresponding StyleItem in the style. An 'h' character
  479.                 makes the text hot, ie the routine searches out text enclosed
  480.                 in angle brackets and draws it in blue underline mode.
  481.             If justification is blank, teJustLeft is used.
  482.             The text field is simply drawn to the screen.
  483.             
  484.             The implementation of this routine is typical "Peter code", ie
  485.             a million line procedure.  I just don't have the guts to
  486.             break it up into more readable chunks.  Sorry.
  487.         *)
  488.         const
  489.             defaultSize = 9;
  490.         var
  491.             defaultFont : integer;
  492.  
  493.             itemRect: Rect;
  494.             oldFont : integer;
  495.             oldSize: integer;
  496.             oldFace: Style;
  497.  
  498.             fixSize: Boolean;
  499.             textIsHot : Boolean;
  500.  
  501.             goingFine : Boolean;
  502.             this: Str255;
  503.             
  504.             fontNumber : integer;
  505.             fontSize : integer;
  506.             fontStyle: Style;
  507.             justification: integer;
  508.  
  509.             stringIndex : integer;
  510.             indexOfEndOfURL : integer;
  511.  
  512.             fi: FontInfo;
  513.             textEditH : TEHandle;
  514.             textStyleRecord : TextStyle;
  515.     begin
  516.         SetPort(dlg);
  517.         
  518.         GetFNum("Geneva", defaultFont);
  519.         
  520.         GetDItemRect(dlg, item, itemRect);
  521.  
  522.         (* Save the current window state. *)
  523.         oldFont := dlg^.txFont;
  524.         oldSize := dlg^.txSize;
  525.         oldFace := dlg^.txFace;
  526.  
  527.         textIsHot := false;
  528.         fixSize := false;
  529.         
  530.         goingFine := Split(':', styledString, this, styledString);
  531.         (* Interpret the font part of styledString. An empty string means
  532.             use the default font, otherwise use the string as the font name.
  533.         *)
  534.         if goingFine then begin
  535.             if this = '' then begin
  536.                 fontNumber := defaultFont;
  537.             end else begin
  538.                 GetFNum(this, fontNumber);
  539.                 if fontNumber = 0 then begin
  540.                     fixSize := true;
  541.                     fontNumber := defaultFont;
  542.                 end; (* if *)
  543.             end; (* if *)
  544.             goingFine := Split(':', styledString, this, styledString);
  545.         end; (* if *)
  546.         
  547.         (* Interpret the size part of styledString. An empty string denotes
  548.             the default size, otherwise evaluate the string and use that size.
  549.         *)
  550.         if goingFine then begin
  551.             if this = '' then begin
  552.                 fontSize := defaultSize;
  553.             end else begin
  554.                 fontSize := DecVal(this);
  555.             end; (* if *)
  556.             goingFine := Split(':', styledString, this, styledString);
  557.         end; (* if *)
  558.  
  559.         (* Interpret the style part of styledString. Loop through the string
  560.             setting styles based on the digits in the style string.
  561.         *)
  562.         if goingFine then begin
  563.             fontStyle := [];
  564.             for stringIndex := 1 to length(this) do begin
  565.                 case this[stringIndex] of
  566.                     '0'..'7':
  567.                         fontStyle := fontStyle + [StyleItem(ord(this[stringIndex]) - ord('0'))];
  568.                     'H', 'h':
  569.                         textIsHot := true;
  570.                     otherwise
  571.                         (* do nothing *);
  572.                 end; (* case *)
  573.             end; (* for *)
  574.             goingFine := Split(':', styledString, this, styledString);
  575.         end; (* if *)
  576.  
  577.         (* Interpret the justification part of styledString. An empty string
  578.             means the default justification (ie teJustLeft) and any other string
  579.             is interpreted as a numeric value suitable for passing to TESetAlignment.
  580.         *)
  581.         if goingFine then begin
  582.             if this = '' then begin
  583.                 justification := teJustLeft;
  584.             end else begin
  585.                 justification := DecVal(this);
  586.             end; (* if *)
  587.             TextFont(fontNumber);
  588.             TextSize(fontSize);
  589.             TextFace(fontStyle);
  590.             
  591.             (* Now fix the text size.  I actually have *no idea* what this is for. *)
  592.             if fixSize then begin
  593.                 GetFontInfo(fi);
  594.                 while (fi.ascent + fi.descent > itemRect.bottom - itemRect.top) do begin
  595.                     if fontSize > 48 then begin
  596.                         fontSize := 48;
  597.                     end else if fontSize > 36 then begin
  598.                         fontSize := 36;
  599.                     end else if fontSize > 27 then begin
  600.                         fontSize := 27;
  601.                     end else if fontSize > 24 then begin
  602.                         fontSize := 24;
  603.                     end else if fontSize > 18 then begin
  604.                         fontSize := 18;
  605.                     end else if fontSize > 14 then begin
  606.                         fontSize := 14;
  607.                     end else if fontSize > 12 then begin
  608.                         fontSize := 12;
  609.                     end else begin
  610.                         fontSize := 9;
  611.                         TextSize(fontSize);
  612.                         leave;
  613.                     end; (* if *)
  614.                     TextSize(fontSize);
  615.                     GetFontInfo(fi);
  616.                 end; (* while *)
  617.             end; (* if *)
  618.             
  619.             (* Now actually go and draw the text. First create a TEStyleHandle. *)
  620.             textEditH := TEStyleNew(itemRect,itemRect);
  621.             if textEditH <> nil then begin
  622.  
  623.                 (* Now install the text and setup the justification. *)
  624.                 TESetText(@styledString[1],length(styledString),textEditH);
  625.                 TESetAlignment(justification, textEditH);
  626.                 
  627.                 (* If the text is hot, search out text enclosed in < > and make it
  628.                     blue underlined.
  629.                 *)
  630.                 if textIsHot then begin
  631.                     for stringIndex := 1 to length(styledString) do begin
  632.                         if styledString[stringIndex] = '<' then begin
  633.                             indexOfEndOfURL := stringIndex + 1;
  634.                             while (indexOfEndOfURL <= length(styledString)) & (styledString[indexOfEndOfURL] <> '>') do begin
  635.                                 indexOfEndOfURL := indexOfEndOfURL + 1;
  636.                             end; (* while *)
  637.                             TESetSelect(stringIndex, indexOfEndOfURL - 1, textEditH);
  638.                             textStyleRecord.tsFace := fontStyle + [underline];
  639.                             textStyleRecord.tsColor.red := 0;
  640.                             textStyleRecord.tsColor.green := 0;
  641.                             textStyleRecord.tsColor.blue := $FFFF;
  642.                             TESetStyle(doFace + doColor, textStyleRecord, false, textEditH);
  643.                         end; (* if *)
  644.                     end; (* for *)
  645.                 end; (* if *)
  646.                 
  647.                 (* Finally, draw the text and dispose the TEStyleHandle. *)
  648.                 TEUpdate(itemRect, textEditH);
  649.                 TEDispose(textEditH);
  650.             end; (* if *)
  651.         end; (* if *)
  652.         
  653.         (* Clean up. *)
  654.         TextFont(oldFont);
  655.         TextSize(oldSize);
  656.         TextFace(oldFace);
  657.     end; (* DrawStyledString *)
  658.  
  659.     procedure ShiftTab (dlg: DialogPtr);
  660.         (* Performs a shift tab operation in the dialog, ie moves the text selection
  661.             to the edit text item immediately before the current one.
  662.             
  663.             This code doesn't deal well with there being no currently selected
  664.             text.  Fortunately that case never comes up, because IC always
  665.             selects the first text item before bringing up a dialog.  Something to
  666.             work on later I guess.
  667.         *)
  668.  
  669.         function IsVisibleEditText(dlg : DialogPtr; item : integer) : Boolean;
  670.             var
  671.                 kind: integer;
  672.         begin
  673.             GetDItemKind(dlg, item, kind);
  674.             IsVisibleEditText := ((kind = editText) & DialogItemHidden(dlg, item));
  675.         end; (* IsVisibleEditText *)
  676.         
  677.         var
  678.             originalItem : integer;
  679.             itemIndex : integer;
  680.             itemCount: integer;
  681.     begin
  682.         originalItem := GetSelectedDialogTextItem(dlg);
  683.         itemCount := CountDItems(dlg);
  684.         
  685.         (* We only have to do work if there's more than one item in the dialog.
  686.             Also bail out of there's no text selected because we don't deal with that case
  687.         *)
  688.         if (originalItem > 0) and (itemCount > 1) then begin
  689.  
  690.             (* Start at the originalItem and walk backwards looking for
  691.                 an unhidden text item.  If itemIndex hits 0, wrap around
  692.                 to the last item. Stop if we get back to the original
  693.                 item or we find an item.
  694.             *)
  695.             itemIndex := originalItem;
  696.             repeat
  697.                 itemIndex := itemIndex - 1;
  698.                 if itemIndex = 0 then begin
  699.                     itemIndex := itemCount;
  700.                 end; (* if *)
  701.             until (itemIndex = originalItem) | IsVisibleEditText(dlg, itemIndex);
  702.             if (itemIndex <> originalItem) & IsVisibleEditText(dlg, itemIndex) then begin
  703.                 SelectDialogItemText(dlg, itemIndex, 0, 32767);
  704.             end; (* if *)
  705.         end; (* if *)
  706.     end; (* ShiftTab *)
  707.  
  708.     procedure FlashDItem (dlg: DialogPtr; item: integer);
  709.         (* Flashes a dialog item by highlighting its control, waiting for 2 ticks
  710.             and then unhighlighting it.
  711.         *)
  712.         var
  713.             junkLong : longint;
  714.     begin
  715.         HiliteControl(GetDControlHandle(dlg, item), kControlButtonPart);
  716.         Delay(2, junkLong);
  717.         HiliteControl(GetDControlHandle(dlg, item), 0);
  718.     end; (* FlashDItem*)
  719.  
  720.     function TrackDItem(window : WindowPtr; item : integer) : Boolean;
  721.         (* Tracks the mouse within the dialog item, returning true
  722.             if the mouse is released inside the item.
  723.         *)
  724.         var
  725.             itemRect : Rect;
  726.             inside : Boolean;
  727.             newInside : Boolean;
  728.             mousePosition : Point;
  729.     begin
  730.         SetPort(window);
  731.         GetDItemRect(window, item, itemRect);
  732.         InvertRect(itemRect);
  733.         inside := true;
  734.         while StillDown do begin
  735.             GetMouse(mousePosition);
  736.             newInside := PtInRect(mousePosition, itemRect);
  737.             if newInside <> inside then begin
  738.                 InvertRect(itemRect);
  739.                 inside := newInside;
  740.             end; (* if *)
  741.         end; (* while *)
  742.         if inside then begin
  743.             InvertRect(itemRect);
  744.         end; (* if *)
  745.         TrackDItem := inside;
  746.     end; (* TrackDItem *)
  747.  
  748.     (* ***** Initialisation ***** *)
  749.     
  750.     procedure InitICDialogs;
  751.     begin
  752.         gDefaultButtonUPP := NewUserItemProc(@DefaultButtonUserItem);
  753.         ICAssert(gDefaultButtonUPP <> nil);
  754.         
  755.         gOKModalFilterUPP := NewModalFilterProc(@OKModalFilter);
  756.         ICAssert(gOKModalFilterUPP <> nil);
  757.         
  758.         gOKCancelModalFilter := NewModalFilterProc(@OKCancelModalFilter);
  759.         ICAssert(gOKCancelModalFilter <> nil);
  760.         
  761.         gOKCancelDiscardModalFilter := NewModalFilterProc(@OKCancelDiscardModalFilter);
  762.         ICAssert(gOKCancelDiscardModalFilter <> nil);
  763.     end; (* InitICDialogs *)
  764.     
  765. end. (* ICDialogs *)